﻿using System;
using System.Windows;

using Microsoft.Phone.Controls;

namespace YourLastOptionsDialog
{
    /// <summary>
    /// The service that allows navigation to the options page as well as its configuration 
    /// and provides extension points for customization.
    /// </summary>
    public class OptionsService
    {
        private static readonly OptionsService _current = new OptionsService();

        private OptionsConfiguration _configuration;

        /// <summary>
        /// Gets the singleton instance of the <see cref="OptionsService"/>.
        /// </summary>
        public static OptionsService Current
        {
            get
            {
                return _current;
            }
        }

        /// <summary>
        /// Gets or sets the current configuration of the <see cref="OptionsService"/>.
        /// </summary>
        internal OptionsConfiguration Configuration
        {
            get
            {
                if (_configuration == null)
                {
                    // use default values
                    _configuration = new OptionsConfiguration();
                }

                return _configuration;
            }

            set
            {
                _configuration = value;
            }
        }

        /// <summary>
        /// Gets or sets the arbitrary object used to generate the options.
        /// </summary>
        internal object Values
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the options view factory that is used to create UI editors for 
        /// the options. By default this uses an instance of the <see cref="DefaultOptionsViewFactory"/>,
        /// which can be overridden by own implementations or classes derived from the default implementation,
        /// to provide custom views for certain option types.
        /// </summary>
        public IOptionsViewFactory OptionsViewFactory
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the options factory extender. This can be set to add support for additional
        /// property types, or to change the behavior of the options service for existing types. If 
        /// an extender is added for new types, it's also required to provide an <see cref="OptionsViewFactory"/>
        /// that serves view implementations for these types. By default, no extender is used, unless
        /// the <c>YourLastOptionsDialog.Extras</c> project is used.
        /// </summary>
        public IOptionsFactoryExtender OptionsFactoryExtender
        {
            get;
            set;
        }

        private OptionsService()
        {
            // can be overridden from the outside
            OptionsViewFactory = new DefaultOptionsViewFactory();
        }

        /// <summary>
        /// Shows the options page using the given values and configuration.
        /// </summary>
        /// <param name="values">The values the options are generated from.</param>
        /// <param name="configuration">The configuration with additional parameters for the <see cref="OptionsService"/>.</param>
        public void Show(object values, OptionsConfiguration configuration)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            if (configuration == null)
            {
                throw new ArgumentNullException("configuration");
            }

            Configuration = configuration;
            Show(values);
        }

        /// <summary>
        /// Shows the options page using the given values.
        /// </summary>
        /// <param name="values">The values the options are generated from.</param>
        public void Show(object values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            // store reference
            Values = values;

            // navigate!
            var frame = Application.Current.RootVisual as PhoneApplicationFrame;
            if (frame == null)
            {
                throw new InvalidOperationException("This phone application has no frame as its root visual.");
            }

            frame.Navigate(new Uri("/YourLastOptionsDialog;component/OptionsPage.xaml", UriKind.Relative));
        }

        /// <summary>
        /// Restores the state of the option page after a real tombstoning situation.
        /// Users of the <see cref="OptionsService"/> are supposed to pass in a reference to the restored
        /// values object after tombstoning. If no updated reference is provided, the options page initiates
        /// a back navigation when it returns from tombstoning.
        /// Note: If the <see cref="OptionsViewFactory"/> and/or <see cref="OptionsFactoryExtender"/> properties are 
        /// used, users of the <see cref="OptionsService"/> are also required to restore these after tombstoning.
        /// </summary>
        /// <param name="values">The values that are restored.</param>
        public void RestoreAfterTombstoning(object values)
        {
            // simply store the values reference
            // => the options page (view model) will check this reference when the options page
            // is being navigated to and re-initialize automatically
            Values = values;
        }

        /// <summary>
        /// Restores the state of the option page after a real tombstoning situation.
        /// Users of the <see cref="OptionsService"/> are supposed to pass in a reference to the restored
        /// values object after tombstoning. If no updated reference is provided, the options page initiates
        /// a back navigation when it returns from tombstoning.
        /// Note: If the <see cref="OptionsViewFactory"/> and/or <see cref="OptionsFactoryExtender"/> properties are 
        /// used, users of the <see cref="OptionsService"/> are also required to restore these after tombstoning.
        /// </summary>
        /// <param name="values">The values that are restored.</param>
        /// <param name="configuration">The configuration with additional parameters for the <see cref="OptionsService"/>
        /// that are restored.</param>
        public void RestoreAfterTombstoning(object values, OptionsConfiguration configuration)
        {
            // simply store the references
            // => the options page (view model) will check these references when the options page
            // is being navigated to and re-initialize automatically
            Values = values;
            Configuration = configuration;
        }
    }
}